package com.ybear.ybutils.utils;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;

import com.ybear.ybutils.utils.handler.Handler;
import com.ybear.ybutils.utils.handler.HandlerManage;

/**
 * 帧动画
 */
public final class FrameAnimation {
    public @interface FrameStatus {
        int INIT = 0;
        int RUNNING = 1;
        int END = 2;
    }

    public interface CallbackTarget {
        void onTarget(@NonNull View v, @NonNull Drawable drawable, int index, int count,
                      @FrameStatus int status);
        void onComplete(@NonNull View v, int count, @FrameStatus int status);
    }
    public static class CallbackTargetAdapter implements CallbackTarget {
        @Override
        public void onTarget(@NonNull View v, @NonNull Drawable drawable, int index, int count,
                             @FrameStatus int status) { }
        @Override
        public void onComplete(@NonNull View v, int count, int status) { }
    }

    private static Handler mHandler;

    private FrameAnimation() {}

    public static FrameRequest create() {
        return create( HandlerManage.create() );
    }

    public static FrameRequest create(Handler h) {
        if( mHandler == null ) mHandler = h;
        return new FrameRequest( mHandler );
    }

    public final static class FrameRequest {
        @NonNull
        private final FrameBuilder mBuilder;
        @NonNull
        private final FrameEntity mData;

        FrameRequest(@NonNull Handler handler) {
            mBuilder = new FrameBuilder( handler, mData = new FrameEntity() );
        }

        public FrameRequest time(long delayMillis) {
            mData.setDelayMillis( delayMillis );
            return this;
        }

        public FrameRequest timeNormal() { return time( 25L ); }

        public FrameRequest loop(int count) {
            mData.setLoopCount( count );
            return this;
        }

        public FrameRequest startFrameIndex(int index) {
            mData.setStartFrameIndex( index );
            return this;
        }

        public FrameRequest endFrameIndex(int index) {
            mData.setEndFrameIndex( index );
            return this;
        }

        public FrameBuilder load(boolean reverseFrame, @NonNull @DrawableRes Integer... resIds) {
            if( reverseFrame ) Utils.reverse( resIds );
            mData.setResIds( resIds );
            return mBuilder;
        }
        public FrameBuilder load(@NonNull @DrawableRes Integer... resIds) {
            return load( false, resIds );
        }

        public FrameBuilder load(boolean reverseFrame, @NonNull Drawable... res) {
            if( reverseFrame ) Utils.reverse( res );
            mData.setRes( res );
            return mBuilder;
        }
        public FrameBuilder load(@NonNull Drawable... res) {
            return load( false, res );
        }

        public FrameBuilder load(Context context, String resName, int startIndex, int count,
                                 boolean reverseFrame) {
            Integer[] resIds = new Integer[ count ];
            startIndex = reverseFrame ? ( resIds.length - 1 ) - startIndex : startIndex;
            for( int i = 0; i < resIds.length; i++ ) {
                resIds[ i ] = ResUtil.getDrawableId(
                        context, resName + ( reverseFrame ? startIndex-- : startIndex++ )
                );
            }
            return load( resIds );
        }
        public FrameBuilder load(Context context, String resName, int startIndex, int count) {
            return load( context, resName, startIndex, count, false );
        }

        public FrameBuilder load(Context context, String resName, int count, boolean reverseFrame) {
            return load( context, resName, 1, count, reverseFrame );
        }
        public FrameBuilder load(Context context, String resName, int count) {
            return load( context, resName, count, false );
        }
    }

    public final static class FrameBuilder {
        @NonNull
        private final FrameControl mControl;
        @NonNull
        private final FrameEntity mData;

        FrameBuilder(Handler handler, @NonNull FrameEntity data) {
            mControl = new FrameControl( handler, mData = data );
        }

        public FrameBuilder setCallbackTarget(CallbackTarget call) {
            mData.setCall( call );
            return this;
        }

        public FrameControl into(@NonNull View v) {
            if( mData.getResLength() == 0 ) {
                throw new NullPointerException("Not set resources data.");
            }
            return mControl.into( v );
        }

        public FrameControl intoOfFullScreen(Window w) {
            return mControl.into( w.findViewById( Window.ID_ANDROID_CONTENT ) );
        }
    }

    public final static class FrameControl implements CallbackTarget {
        private final Handler mHandler;
        private View mView;
        @NonNull
        private final FrameEntity mData;

        private int mStatus = 0;             //-1：停止，0：暂停，1：播放
        private int mCurLoopCount = 0;       //当前循环次数
        private boolean isNow = false;       //是否立即处理操作

        FrameControl(Handler handler, @NonNull FrameEntity data) {
            mHandler = handler;
            mData = data;
        }

        /**
         * 回调的Target
         * @param v     处理的View
         */
        @Override
        public void onTarget(@NonNull View v, @NonNull Drawable drawable,
                             int index, int count, @FrameStatus int status) {
            if( v instanceof ImageView ) {
                ((ImageView)v).setImageDrawable( drawable );
            } else {
                if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN ) {
                    v.setBackground( drawable );
                }else {
                    v.setBackgroundDrawable( drawable );
                }
            }
            v.requestLayout();

            CallbackTarget call = mData.getCall();
            if( call != null ) call.onTarget( mView, drawable, index, count, status );
        }

        @Override
        public void onComplete(@NonNull View v, int count, @FrameStatus int status) {
            CallbackTarget call = mData.getCall();
            if( call != null ) call.onComplete( mView, count, status );
        }

        FrameControl into(@NonNull View v) {
            Context context = v.getContext();
            mView = v;
//            //没有设置Target时，使用默认的Target
//            if( mData.getCall() == null ) mData.setCall( this );
            //首次设置默认帧
            mHandler.post(() -> showFrame( context, mData.getStartFrameIndex(), FrameStatus.INIT ));
            return this;
        }

        public boolean isPause() { return mStatus == 2; }

        public boolean isStop() { return mStatus == -1; }

        public synchronized void play(Context context) {
            if( mStatus == 1 ) return;
            mStatus = 1;
            mHandler.post(() -> {
                if( mData.getResLength() == 1 ) {
                    //显示第一帧
                    showFrame( context, 0, FrameStatus.END );
                }else {
                    nextImg( context );
                }
            });
        }

        public void pause() {
            mStatus = 2;
            isNow = false;
        }

        public void pauseNow() {
            mStatus = 2;
            isNow = true;
        }

        public void stop() {
            mStatus = -1;
            isNow = false;
        }

        public void stopNow() {
            mStatus = -1;
            isNow = true;
        }

        private void nextImg(Context context) {
            int status = mStatus;
            int nextIndex = mData.getNextIndex();
            int loopCount = mData.getLoopCount();
            int resLen, endIndex;
            //到达最后一帧
            boolean isLastIndex = nextIndex >= mData.getResLength();

            if( isLastIndex ) {
                //处理循环
                if( loopCount != -1 && ++mCurLoopCount >= loopCount ) {
                    /* 停止循环 */
                    mCurLoopCount = 0;
                    status = mStatus = -1;
                }
                //重置到第一帧
                mData.setNextIndex( nextIndex = 0 );
            }else {
                //移动到下一帧
                mData.setNextIndex( nextIndex + 1 );
            }
            //资源数量
            resLen = mData.getResLength();
            //最后展示帧
            endIndex = mData.getEndFrameIndex();
            //未设置时初始化
            if( endIndex < 0 || endIndex >= resLen ) endIndex = resLen - 1;

            //非now的情况下需要等到整个帧播放完毕
            if( !isNow && !isLastIndex && nextIndex != endIndex ) status = 1;
            //是否正在播放
            switch ( status ) {
                case -1:                //停止
                    //播放当前帧
                    showFrame( context, endIndex, FrameStatus.END );
                    /* 初始化 */
                    mData.setNextIndex( 0 );
                    isNow = false;
                    break;
                case 1:                 //播放
                    //播放当前帧
                    showFrame( context, nextIndex, FrameStatus.RUNNING );
                    //处理下一帧
                    mHandler.post( () -> nextImg( context ), mData.getDelayMillis() );
                    break;
            }
        }

        /**
         * 显示帧
         */
        private void showFrame(Context context, int index, @FrameStatus int status) {
            if( index < 0 ) return;
            Drawable drawable = null;
            Drawable[] ds = mData.getRes();
            Integer[] ids = mData.getResIds();
            if( ds == null || ds.length == 0 ) {
                //id
                if( ids != null && ids.length > 0 && index < ids.length ) {
                    drawable = ResUtil.getDrawable( context, ids[ index ] );
                }
            }else {
                //drawable
                drawable = ds[ index ];
            }
            if( drawable == null ) return;
            int count = mData.getResLength();
            onTarget( mView, drawable, index, count, status );
            if( index >= count - 1 ) onComplete( mView, count, status );
        }
    }

    private final static class FrameEntity {
        private Drawable[] res;
        private Integer[] resIds;
        private int nextIndex = 0;
        private int loopCount = -1;
        private long delayMillis = 250;
        private int startFrameIndex = 0;
        private int endFrameIndex = -1;
        private CallbackTarget mCall;

        int getResLength() {
            return res != null && res.length > 0 ?
                    res.length : resIds != null && resIds.length > 0 ?
                    resIds.length : 0;
        }

        Drawable[] getRes() { return res; }
        void setRes(Drawable[] res) { this.res = res; }

        Integer[] getResIds() { return resIds; }
        void setResIds(Integer[] resIds) { this.resIds = resIds; }

        int getNextIndex() { return nextIndex; }
        void setNextIndex(int index) { nextIndex = index; }

        int getLoopCount() { return loopCount; }
        void setLoopCount(int count) { loopCount = count; }

        long getDelayMillis() { return delayMillis; }
        void setDelayMillis(long delayMillis) { this.delayMillis = delayMillis; }

        int getStartFrameIndex() { return startFrameIndex; }
        void setStartFrameIndex(int index) { startFrameIndex = index; }

        int getEndFrameIndex() { return endFrameIndex; }
        void setEndFrameIndex(int index) { endFrameIndex = index; }

        CallbackTarget getCall() { return mCall; }
        void setCall(CallbackTarget call) { mCall = call; }
    }
}
